<?php namespace Poscms\Autoloader;
/**
 * POSCMS框架文件
 * 二次开发时请勿修改本文件
 * 四川迅睿云软件开发有限公司 www.xunruicms.com
 */

class Autoloader
{

    /**
     * Stores namespaces as key, and path as values.
     *
     * @var array
     */
    protected $prefixes = array();

    /**
     * Stores class name as key, and path as values.
     *
     * @var array
     */
    protected $classmap = array();


    //--------------------------------------------------------------------

    /**
     * Register the loader with the SPL autoloader stack.
     *
     * @codeCoverageIgnore
     */
    public function register()
    {

        $this->prefixes = array(

            'Poscms\Home'      =>    CMSPATH.'control/home',
            'Poscms\Admin'     =>    CMSPATH.'control/admin',
            'Poscms\Member'    =>   CMSPATH.'control/member',
            'Poscms\Model'     =>    CMSPATH.'model',
            'Poscms\Library'   =>   CMSPATH.'library',
            'Phpcmf\Library'   =>   CMSPATH.'fcms/library',
            'Phpcmf\Model'     =>    CMSPATH.'model',
        );

        /**
         * -------------------------------------------------------------------
         * Class Map
         * -------------------------------------------------------------------
         * The class map provides a map of class names and their exact
         * location on the drive. Classes loaded in this manner will have
         * slightly faster performance because they will not have to be
         * searched for within one or more directories as they would if they
         * were being autoloaded through a namespace.
         *
         * Prototype:
         *
         *   $Config['classmap'] = [
         *       'MyClass'   => '/path/to/class/file.php'
         *   ];
         */
        $this->classmap = array(

            'Poscms\Common'         =>    CMSPATH.'core/Common.php',
            'Poscms\Service'        =>    CMSPATH.'core/Service.php',
            'Poscms\Model'          =>    CMSPATH.'core/Model.php',
            'Poscms\App'            =>    CMSPATH.'core/App.php',
            'Poscms\Module'         =>    CMSPATH.'core/Module.php',
            'CI_Model'               =>    BASEPATH.'core/Model.php',
            'Phpcmf\Service'        =>    CMSPATH.'core/Phpcmf.php',
            'Phpcmf\App'            =>    CMSPATH.'fcms/App.php',
            'Phpcmf\Model'            =>    CMSPATH.'fcms/Model.php',
            'Phpcmf\Table'            =>    CMSPATH.'fcms/Table.php',

        );

        // Since the default file extensions are searched
        // in order of .inc then .php, but we always use .php,
        // put the .php extension first to eek out a bit
        // better performance.
        // http://php.net/manual/en/function.spl-autoload.php#78053
        spl_autoload_extensions('.php,.inc');

        // Prepend the PSR4  autoloader for maximum performance.
        spl_autoload_register(array($this, 'loadClass'), true, true);

        // Now prepend another loader for the files in our class map.
        $config = is_array($this->classmap) ? $this->classmap : array();

        spl_autoload_register(function ($class) use ($config) {
            if ( ! array_key_exists($class, $config))
            {
                return false;
            }

            include_once $config[$class];
        }, true, // Throw exception
            true // Prepend
        );
    }

    //--------------------------------------------------------------------

    /**
     * Registers a namespace with the autoloader.
     *
     * @param string $namespace
     * @param string $path
     *
     * @return Autoloader
     */
    public function addNamespace( $namespace,  $path)
    {
        if (isset($this->prefixes[$namespace]))
        {
            if (is_string($this->prefixes[$namespace]))
            {
                $this->prefixes[$namespace] = array($this->prefixes[$namespace]);
            }

            $this->prefixes[$namespace] = array_merge($this->prefixes[$namespace], array($path));
        }
        else
        {
            $this->prefixes[$namespace] = array($path);
        }


        return $this;
    }

    //--------------------------------------------------------------------

    /**
     * Removes a single namespace from the psr4 settings.
     *
     * @param string $namespace
     *
     * @return Autoloader
     */
    public function removeNamespace( $namespace)
    {
        unset($this->prefixes[$namespace]);

        return $this;
    }

    //--------------------------------------------------------------------

    /**
     * Loads the class file for a given class name.
     *
     * @param string $class The fully qualified class name.
     *
     * @return mixed            The mapped file on success, or boolean false
     *                          on failure.
     */
    public function loadClass( $class)
    {
        $class = trim($class, '\\');
        $class = str_ireplace('.php', '', $class);

        $mapped_file = $this->loadInNamespace($class);

        // Nothing? One last chance by looking
        // in common CodeIgniter folders.
        if ( ! $mapped_file)
        {
            $mapped_file = $this->loadLegacy($class);
        }

        return $mapped_file;
    }

    //--------------------------------------------------------------------

    /**
     * Loads the class file for a given class name.
     *
     * @param string $class The fully-qualified class name
     *
     * @return mixed            The mapped file name on success, or boolean false on fail
     */
    protected function loadInNamespace( $class)
    {
        if (strpos($class, '\\') === false)
        {
            return false;
        }

        foreach ($this->prefixes as $namespace => $directories)
        {
            if (is_string($directories))
            {
                $directories = array($directories);
            }

            foreach ($directories as $directory)
            {
                $directory = rtrim($directory, '/');

                if (strpos($class, $namespace) === 0)
                {
                    $filePath = $directory . str_replace('\\', '/', substr($class, strlen($namespace))) . '.php';
                    $filename = $this->requireFile($filePath);

                    if ($filename)
                    {
                        return $filename;
                    }
                }
            }
        }

        // never found a mapped file
        return false;
    }

    //--------------------------------------------------------------------

    /**
     * Attempts to load the class from common locations in previous
     * version of CodeIgniter, namely 'application/libraries', and
     * 'application/Models'.
     *
     * @param string $class The class name. This typically should NOT have a namespace.
     *
     * @return mixed    The mapped file name on success, or boolean false on failure
     */
    protected function loadLegacy( $class)
    {
        // If there is a namespace on this class, then
        // we cannot load it from traditional locations.
        if (strpos($class, '\\') !== false)
        {
            return false;
        }

        $paths = array(
            APPPATH . 'Controllers/',
            APPPATH . 'Libraries/',
            APPPATH . 'Models/',
        );

        $class = str_replace('\\', '/', $class) . '.php';

        foreach ($paths as $path)
        {
            if ($file = $this->requireFile($path . $class))
            {
                return $file;
            }
        }

        return false;
    }

    //--------------------------------------------------------------------

    /**
     * A central way to require a file is loaded. Split out primarily
     * for testing purposes.
     *
     * @codeCoverageIgnore
     *
     * @param string $file
     *
     * @return bool
     */
    protected function requireFile( $file)
    {
        $file = $this->sanitizeFilename($file);

        if (file_exists($file))
        {
            require_once $file;

            return $file;
        }

        return false;
    }

    //--------------------------------------------------------------------

    /**
     * Sanitizes a filename, replacing spaces with dashes.
     *
     * Removes special characters that are illegal in filenames on certain
     * operating systems and special characters requiring special escaping
     * to manipulate at the command line. Replaces spaces and consecutive
     * dashes with a single dash. Trim period, dash and underscore from beginning
     * and end of filename.
     *
     * @param string $filename
     *
     * @return string       The sanitized filename
     */
    public function sanitizeFilename( $filename)
    {
        // Only allow characters deemed safe for POSIX portable filenames.
        // Plus the forward slash for directory separators since this might
        // be a path.
        // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_278
        // Modified to allow backslash and colons for on Windows machines.
        $filename = preg_replace('/[^a-zA-Z0-9\s\/\-\_\.\:\\\\]/', '', $filename);

        // Clean up our filename edges.
        $filename = trim($filename, '.-_');

        return $filename;
    }

    //--------------------------------------------------------------------
}
